package com.mars.module.workflow.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.common.collect.Lists;
import com.mars.common.enums.CounterTypeEnum;
import com.mars.framework.exception.ServiceException;
import com.mars.module.workflow.entity.ActHiOpinion;
import com.mars.module.workflow.request.NextStepRequest;
import com.mars.module.workflow.response.ActHiOpinionResponse;
import com.mars.module.workflow.response.FlowElementResponse;
import com.mars.module.workflow.response.NextNodeResponse;
import com.mars.module.workflow.service.IProcessNodeService;
import de.odysseus.el.util.SimpleContext;
import lombok.AllArgsConstructor;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.activiti.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ExecutionQuery;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.apache.el.ExpressionFactoryImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 功能描述
 *
 * @author 程序员Mars
 * @version 1.0
 * @date 2024-02-01 09:23:45
 */
@Service
@AllArgsConstructor
public class ProcessNodeServiceImpl implements IProcessNodeService {

    private final HistoryService historyService;

    private final RepositoryService repositoryService;

    private final RuntimeService runtimeService;

    private final TaskService taskService;

    @Override
    public FlowElement queryNextStepInfo(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        // 取得已提交的任务
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery()
                .taskId(task.getId()).singleResult();
        // 获得流程定义
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(historicTaskInstance.getProcessDefinitionId());
        //获得当前流程的活动ID
        ExecutionQuery executionQuery = runtimeService.createExecutionQuery();
        Execution execution = executionQuery.executionId(historicTaskInstance.getExecutionId()).singleResult();
        String activityId = execution.getActivityId();
        UserTask userTask;
        while (true) {
            //根据活动节点获取当前的组件信息
            FlowNode flowNode = getFlowNode(processDefinition.getId(), activityId);
            //获取该节点之后的流向
            List<SequenceFlow> sequenceFlowListOutGoing = flowNode.getOutgoingFlows();
            // 获取的下个节点不一定是userTask的任务节点，所以要判断是否是任务节点
            if (sequenceFlowListOutGoing.size() > 1) {
                // 如果有1条以上的出线，表示有分支，需要判断分支的条件才能知道走哪个分支
                // 遍历节点的出线得到下个activityId
                activityId = getNextActivityId(execution.getId(), task.getProcessInstanceId(), sequenceFlowListOutGoing);
            } else if (sequenceFlowListOutGoing.size() == 1) {
                // 只有1条出线,直接取得下个节点
                SequenceFlow sequenceFlow = sequenceFlowListOutGoing.get(0);
                // 下个节点
                FlowElement flowElement = sequenceFlow.getTargetFlowElement();
                if (flowElement instanceof UserTask) {
                    // 下个节点为UserTask时
                    userTask = (UserTask) flowElement;
                    return userTask;
                } else if (flowElement instanceof ExclusiveGateway) {
                    // 下个节点为排它网关时
                    ExclusiveGateway exclusiveGateway = (ExclusiveGateway) flowElement;
                    List<SequenceFlow> outgoingFlows = exclusiveGateway.getOutgoingFlows();
                    // 遍历网关的出线得到下个activityId
                    activityId = getNextActivityId(execution.getId(), task.getProcessInstanceId(), outgoingFlows);
                    //找到出口的节点
                    flowNode = getFlowNode(processDefinition.getId(), activityId);
                    //找不到符合条件的节点，就返回null
                    if (flowNode == null) {
                        return null;
                    }
                    //如果出口处就是一个UserTask，就返回它
                    if (flowNode instanceof UserTask) {
                        userTask = (UserTask) flowNode;
                        return userTask;
                    }
                    //如果出口处是结束节点，则返回空
                    if (flowNode instanceof EndEvent) {
                        return null;
                    }
                } else {
                    // todo
                    return null;
                }
            } else {
                // 没有出线，则表明是结束节点
                return null;
            }
        }
    }

    @Override
    public List<FlowElementResponse> getNodeList(String deploymentId, String definitionKey) {
        //流程定义ids
        List<ProcessDefinition> processDefinition = ProcessEngines.getDefaultProcessEngine()
                .getRepositoryService().createProcessDefinitionQuery()
                .deploymentId(deploymentId)
                .processDefinitionKey(definitionKey)
                .list();
        BpmnModel bpmnModel = ProcessEngines.getDefaultProcessEngine().getRepositoryService().getBpmnModel(processDefinition.get(0).getId());
        Process process = bpmnModel.getProcesses().get(0);
        //获取所有节点
        Collection<FlowElement> flowElements = process.getFlowElements();
        List<FlowElement> list = flowElements.stream().filter(x -> StringUtils.isNotEmpty(x.getName()) && StringUtils.isNotEmpty(x.getId())).collect(Collectors.toList());
        return list.stream().map(x -> {
            FlowElementResponse response = new FlowElementResponse();
            BeanUtils.copyProperties(x, response);
            return response;
        }).collect(Collectors.toList());
    }


    /**
     * 根据el表达式取得满足条件的下一个activityId
     *
     * @param executionId
     * @param processInstanceId
     * @param outgoingFlows
     * @return
     */
    public String getNextActivityId(String executionId, String processInstanceId, List<SequenceFlow> outgoingFlows) {
        String activityId = null;
        // 遍历出线
        for (SequenceFlow outgoingFlow : outgoingFlows) {
            // 取得线上的条件
            String conditionExpression = outgoingFlow.getConditionExpression();
            // 取得所有变量
            Map<String, Object> variables = runtimeService.getVariables(executionId);
            String variableName = "";
            // 判断网关条件里是否包含变量名
            for (String s : variables.keySet()) {
                if (conditionExpression.contains(s)) {
                    // 找到网关条件里的变量名
                    variableName = s;
                }
            }
            String conditionVal = getVariableValue(variableName, processInstanceId);

            // 判断el表达式是否成立
            if (isCondition(variableName, conditionExpression, conditionVal)) {
                // 取得目标节点
                FlowElement targetFlowElement = outgoingFlow.getTargetFlowElement();
                activityId = targetFlowElement.getId();
                continue;
            }
        }
        return activityId;
    }

    /**
     * 根据活动节点和流程定义ID获取该活动节点的组件信息
     */
    public FlowNode getFlowNode(String processDefinitionId, String flowElementId) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(flowElementId);
        return flowNode;
    }


    /**
     * 根据key和value判断el表达式是否通过
     *
     * @param key   el表达式key
     * @param el    el表达式
     * @param value el表达式传入值
     * @return
     */
    public boolean isCondition(String key, String el, String value) {
        ExpressionFactory factory = new ExpressionFactoryImpl();
        SimpleContext context = new SimpleContext();
        context.setVariable(key, factory.createValueExpression(value, String.class));
        ValueExpression e = factory.createValueExpression(context, el, boolean.class);
        return (Boolean) e.getValue(context);
    }


    /**
     * 取得流程变量的值
     *
     * @param variableName      变量名
     * @param processInstanceId 流程实例Id
     * @return
     */
    public String getVariableValue(String variableName, String processInstanceId) {
        Execution execution = runtimeService
                .createExecutionQuery().processInstanceId(processInstanceId).list().get(0);
        Object object = runtimeService.getVariable(execution.getId(), variableName);
        return object == null ? "" : object.toString();
    }

    /**
     * @param list         存放网关节点
     * @param flowNodeList 任务节点
     * @description 解析当前节点的接下来的任务节点
     */
    private void addNextNode(List<FlowNode> list, List<FlowNode> flowNodeList) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        List<FlowNode> flowNodeList1 = Lists.newArrayList();
        for (FlowNode flowNode : list) {
            List<SequenceFlow> sequenceFlows = flowNode.getOutgoingFlows();
            for (SequenceFlow sequenceFlow : sequenceFlows) {
                FlowNode flowNode1 = (FlowNode) sequenceFlow.getTargetFlowElement();
                if (flowNode1 == null) {
                    continue;
                }
                if (flowNode1 instanceof UserTask) {
                    if (flowNode1.getName() == null) {
                        flowNode1.setName(" ");
                    }
                    flowNodeList.add(flowNode1);
                } else if (flowNode1 instanceof EndEvent) {
                    flowNode1.setName("结束");
                    flowNodeList.add(flowNode1);
                } else if (flowNode1 instanceof StartEvent) {
                    flowNode1.setName("开始");
                    flowNodeList.add(flowNode1);
                } else {
                    flowNodeList1.add(flowNode1);
                }
            }
        }
        addNextNode(flowNodeList1, flowNodeList);

    }
}
